/*
 * Copyright (C) 2008-2010 Martin Riesz <riesz.martin at gmail.com>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.pneditor.editor.commands;

import java.awt.Point;
import java.util.HashSet;
import java.util.Set;
import org.pneditor.petrinet.Arc;
import org.pneditor.petrinet.Element;
import org.pneditor.petrinet.PetriNet;
import org.pneditor.petrinet.PlaceNode;
import org.pneditor.petrinet.ReferencePlace;
import org.pneditor.petrinet.ReferenceArc;
import org.pneditor.petrinet.Subnet;
import org.pneditor.petrinet.Transition;
import org.pneditor.util.CollectionTools;
import org.pneditor.util.Command;

/**
 *
 * @author Martin Riesz <riesz.martin at gmail.com>
 */
public class ConvertTransitionToSubnetCommand implements Command {
	private Transition transition;
	private Subnet parentSubnet;
	private Subnet subnet;
	private Set<ReferencePlace> referencePlaces = new HashSet<ReferencePlace>();
	private Set<ReferenceArc> referenceArcs = new HashSet<ReferenceArc>();
	private Set<Arc> realArcs = new HashSet<Arc>();
	private PetriNet petriNet;
	private String transitionOldLabel;

	public ConvertTransitionToSubnetCommand(Transition transition, PetriNet petriNet) {
		this.transition = transition;
		this.parentSubnet = transition.getParentSubnet();
		this.petriNet = petriNet;
	}
	
	public void execute() {
		subnet = new Subnet();
		subnet.setCenter(transition.getCenter().x, transition.getCenter().y);
		petriNet.getNodeSimpleIdGenerator().setUniqueId(subnet);
		for (PlaceNode parentPlaceNode : transition.getConnectedPlaceNodes()) {
			
			ReferencePlace referencePlace = new ReferencePlace(parentPlaceNode);
			referencePlace.setCenter(
				parentPlaceNode.getCenter().x - transition.getCenter().x,
				parentPlaceNode.getCenter().y - transition.getCenter().y
			);
			petriNet.getNodeSimpleIdGenerator().setUniqueId(referencePlace);
			referencePlaces.add(referencePlace);
			
			realArcs.addAll(parentPlaceNode.getConnectedArcs(transition));
			
			Arc realArc = CollectionTools.getFirstElement(parentPlaceNode.getConnectedArcs(transition));
			ReferenceArc referenceArc = new ReferenceArc(parentPlaceNode, subnet);
			referenceArc.setPlaceToTransition(realArc.isPlaceToTransition());
			referenceArc.setBreakPoints(realArc.getBreakPoints());
			referenceArcs.add(referenceArc);
			
			subnet.addElement(referencePlace);
		}
		transitionOldLabel = transition.getLabel();
		redo();
	}

	public void undo() {
		transition.setLabel(transitionOldLabel);
		parentSubnet.removeElement(subnet);
		migrateElementToSubnet(transition, parentSubnet);
		transition.setCenter(subnet.getCenter().x, subnet.getCenter().y);
		for (Arc realArc : realArcs) {
			for (Point breakPoint : realArc.getBreakPoints()) {
				breakPoint.translate(
					subnet.getCenter().x, subnet.getCenter().y
				);
			}
			migrateElementToSubnet(realArc, parentSubnet);
			
			ReferencePlace referencePlace = (ReferencePlace)realArc.getPlaceNode();
			realArc.setPlaceNode(referencePlace.getConnectedPlaceNode());
		}
		
		for (ReferenceArc referenceArc : referenceArcs) {
			parentSubnet.removeElement(referenceArc);
		}
	}
	
	public void redo() {
		petriNet.getNodeLabelGenerator().setLabelsOfConversionTransitionToSubnet(transition, subnet);
		
		parentSubnet.addElement(subnet);
		migrateElementToSubnet(transition, subnet);
		transition.setCenter(0, 0);
		for (Arc realArc : realArcs) {
			for (Point breakPoint : realArc.getBreakPoints()) {
				breakPoint.translate(
					-subnet.getCenter().x, -subnet.getCenter().y
				);
			}
			migrateElementToSubnet(realArc, subnet);
			
			ReferencePlace referencePlace = getReferencePlace(realArc.getPlaceNode());
			realArc.setPlaceNode(referencePlace);
		}
		for (ReferenceArc referenceArc : referenceArcs) {
			parentSubnet.addElement(referenceArc);
		}
		
	}
	
	private ReferencePlace getReferencePlace(PlaceNode placeNode) {
		for (ReferencePlace referencePlace : referencePlaces) {
			if (referencePlace.getConnectedPlaceNode() == placeNode) {
				return referencePlace;
			}
		}
		throw new RuntimeException("ConvertTransitionToSubnetCommand: PlaceNode: missing ReferencePlace");
	}
	
	private void migrateElementToSubnet(Element element, Subnet subnet) {
		element.getParentSubnet().removeElement(element);
		subnet.addElement(element);
		element.setParentSubnet(subnet);
	}

	@Override
	public String toString() {
		return "Convert transition to subnet";
	}
	
}
